﻿namespace PerformanceTests.DataAccess
{
    using System.Diagnostics;
    using System.Linq;
    using FlexConta.DataAccess.DataModel;
    using System.Collections.Generic;
    using System;
    using System.Data.Entity;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System.Data.Common;
    using System.Data.SqlClient;
    using System.Data.Entity.Infrastructure;

    [TestClass]
    public class EnitiesCRUD
    {
        /// <summary>
        /// Testing CRUD (Create/Update/Delete) operations for Adresa, wihout filling the object properties (like Judet, Localitate).
        /// </summary>
        [TestMethod]
        public void CRUD_Adresa_NoDependencies()
        {
            const int entitiesCount = 1000;

            TestTimeCounter timeCounter = new TestTimeCounter();

            Debug.WriteLine("");
            Debug.WriteLine("Performance test: Add " + entitiesCount.ToString() + " entities Adresa");

            // Create the DB context and Keep the connection allways open
            string connectionString = "testDb3.sdf";
            DataContext context = new DataContext(connectionString);
            ((IObjectContextAdapter)context).ObjectContext.Connection.Open();

            // 1. Create the entities, and add them to a collection. This is not needed to be measured.
            List<Adresa> adreseList = new List<Adresa>();
            for (var i = 0; i <= entitiesCount-1; i++)
            {
                Adresa oAdresa = new Adresa ();
                oAdresa.Numar = i.ToString() ;
                oAdresa.Strada= "Strada " + i;
                oAdresa.Detalii = "Detalii " + i;

                adreseList.Add(oAdresa);          
            }

            // start the timer
            timeCounter.StartTest();

            // 2. Add to the context all the entities created and stored in the list from the previous step. Measure.
            Debug.WriteLine("Adding {0} entities ------------------------------------------", entitiesCount);
            timeCounter.StartSession();
            foreach (var oAdresa in adreseList)
            {
                context.Adrese.Add(oAdresa);
            }
            timeCounter.StopSession();
            Debug.WriteLine(" > Adding to context takes {0};", timeCounter.LastSessionDuration);
            saveAndMeasure(context, timeCounter);
            
            // 3. Get back all the entities, one by one, from the DB to another list.
            Debug.WriteLine("Retrieving {0} entities individually -------------------------", entitiesCount);
            var savedAdrese = new List<Adresa>();
            timeCounter.StartSession();
            foreach (var ramAdresa in adreseList)
            {
                Adresa savedAdresa = context.Adrese.FirstOrDefault(a => a.InternalId == ramAdresa.InternalId);
                savedAdrese.Add(savedAdresa);
            }
            timeCounter.StopSession();
            Debug.WriteLine(" > Retrieving takes {0}.", timeCounter.LastSessionDuration);

            // 4. Update each entity in the context (change one property, ex. the number). Measure
            Debug.WriteLine("Updating {0} entities individually -------------------------", entitiesCount);
            timeCounter.StartSession();
            foreach (var a in adreseList)
            {
                context.Adrese.Attach(a);
                context.Entry(a).State = System.Data.EntityState.Modified;
                a.Numar = a.Numar + "x";
            }
            timeCounter.StopSession();
            Debug.WriteLine(" > Updating entities in context takes {0};", timeCounter.LastSessionDuration);
            saveAndMeasure(context, timeCounter);
            
            // 5. Delete all the entities from context. Measure
            Debug.WriteLine("Deleting {0} entities individually -------------------------", entitiesCount);
            timeCounter.StartSession();
            foreach (var a in adreseList)
            {
                context.Adrese.Remove(a);
            }
            timeCounter.StopSession();
            Debug.WriteLine(" > Deleting from context takes {0};", timeCounter.LastSessionDuration);
            saveAndMeasure(context, timeCounter);

            timeCounter.StopTest();
            Debug.WriteLine(">> Total test duration was {0};", timeCounter.LastTestDuration);
            
            // close the connection
            ((IObjectContextAdapter)context).ObjectContext.Connection.Close();

            Debug.WriteLine(" --- End of Adresa_NoDependencies test --- ");
        }

        /// <summary>
        /// Testing CRUD (Create/Update/Delete) operations for Adresa, while filling one of the object properties (like Judet, Localitate).
        /// </summary>
        [TestMethod]
        public void CRUD_Adresa_OneDependency()
        {
            // repeat the previous test 
            const int entitiesCount = 1000;

            TestTimeCounter timeCounter = new TestTimeCounter();

            Debug.WriteLine("");
            Debug.WriteLine("Performance test: Add " + entitiesCount.ToString() + " entities Adresa");

            // Create the DB context and Keep the connection allways open
            string connectionString = "testDb3.sdf";
            DataContext context = new DataContext(connectionString);
            ((IObjectContextAdapter)context).ObjectContext.Connection.Open();



            // 1. Create the entities, and add them to a collection. This is not needed to be measured.
            List<Adresa> adreseList = new List<Adresa>();
            for (var i = 0; i <= entitiesCount - 1; i++)
            {
                Adresa oAdresa = new Adresa();
                oAdresa.Judet = new Localitate(); //dep

                oAdresa.Numar = i.ToString();
                oAdresa.Strada = "Strada " + i;
                oAdresa.Detalii = "Detalii " + i;
                oAdresa.Judet.Denumire = "Judet" + i; //dep

                adreseList.Add(oAdresa);
            }

            // start the timer
            timeCounter.StartTest();

            // 2. Add to the context all the entities created and stored in the list from the previous step. Measure.
            Debug.WriteLine("Adding {0} entities ------------------------------------------", entitiesCount);
            timeCounter.StartSession();
            foreach (var oAdresa in adreseList)
            {
                context.Adrese.Add(oAdresa);
            }
            timeCounter.StopSession();
            Debug.WriteLine(" > Adding to context takes {0};", timeCounter.LastSessionDuration);
            saveAndMeasure(context, timeCounter);

            // 3. Get back all the entities, one by one, from the DB to another list.
            Debug.WriteLine("Retrieving {0} entities individually -------------------------", entitiesCount);
            var savedAdrese = new List<Adresa>();
            timeCounter.StartSession();
            foreach (var ramAdresa in adreseList)
            {

                Adresa savedAdresa = context.Adrese.Include(a => a.Judet).FirstOrDefault(a => a.InternalId == ramAdresa.InternalId); //dep
                savedAdrese.Add(savedAdresa);
            }
            timeCounter.StopSession();
            Debug.WriteLine(" > Retrieving takes {0}.", timeCounter.LastSessionDuration);

            // 4. Update each entity in the context (change one property, ex. the number). Measure
            Debug.WriteLine("Updating {0} entities individually -------------------------", entitiesCount);
            timeCounter.StartSession();
            foreach (var a in adreseList)
            {
                context.Adrese.Attach(a);
                context.Entry(a.Judet).State = System.Data.EntityState.Modified;
                //a.Numar = a.Numar + "x";
                a.Judet.Denumire = a.Judet.Denumire + "x";

            }
            timeCounter.StopSession();
            Debug.WriteLine(" > Updating entities in context takes {0};", timeCounter.LastSessionDuration);
            saveAndMeasure(context, timeCounter);

            // 5. Delete all the entities from context. Measure
            Debug.WriteLine("Deleting {0} entities individually -------------------------", entitiesCount);
            timeCounter.StartSession();
            foreach (var a in adreseList)
            {
                context.Adrese.Remove(a);
            }
            timeCounter.StopSession();
            Debug.WriteLine(" > Deleting from context takes {0};", timeCounter.LastSessionDuration);
            saveAndMeasure(context, timeCounter);

            timeCounter.StopTest();
            Debug.WriteLine(">> Total test duration was {0};", timeCounter.LastTestDuration);

            // close the connection
            ((IObjectContextAdapter)context).ObjectContext.Connection.Close();

            Debug.WriteLine(" --- End of Adresa_OneDependency test --- ");
        }

        private void saveAndMeasure(DataContext context, TestTimeCounter timeCounter)
        {
            // 3. Save to the Db. Measure
            timeCounter.StartSession();
            var affected = context.SaveChanges();
            timeCounter.StopSession();
            Debug.WriteLine(" > DB save takes {0}; {1} entities affected.", timeCounter.LastSessionDuration, affected);
        }

    }
}
